package cards;

import java.util.ArrayList;
import java.util.List;

/**
 * Logic for the card game "Prsi"
 * @author Vlada
 */
public class PrsiGame implements CardGameIface {
    
    //List of players
    private final List<Player> players;
    //Player on move
    private Player playerOnMove;
    //Present suit - suit the players are allowed to play
    private Suit presentSuit;
    //Present rank - rank the players are allowed to play
    private Rank presentRank;
    //State of the game; may change after each player's turn
    private GameState gameState;
    //Number of SEDM ranks in a serie
    private int sedmSuccession;
    
    // Playing deck and discard pile as final
    private final Deck playingDeck;
    private final DiscardPile discardPile;
    
 
    
    /**
     * Constructor
     * @param playersNames - names of the players
     * @param numberOfCards - number of cards for each player
     */
    public PrsiGame(String[] playersNames, int numberOfCards) {
        players = new ArrayList<>();
        playingDeck = new Deck();
        discardPile = new DiscardPile();
        sedmSuccession = 0;
        
        //TODO - check that the number of cards * number of players < number of cards in the deck
        for (int i = 0; i < playersNames.length; i++) {
            players.add(new Player(playersNames[i], numberOfCards));
        }
        //shuffle
        playingDeck.shuffle();
        //set player on move
        playerOnMove = players.get(0);   
        deal(numberOfCards);
        gameState = GameState.Continue;
    }
    
    /**
     * Distributes cards to the players and one to the discardPile on the table
     * 
     * @param numberOfCards - number of cards to be distributed to each player
     */
    private void deal(int numberOfCards) {
        for (int i = 0; i < numberOfCards; i++) {
            for (Player player : players) {
                player.getHand().addCard(playingDeck.removeTop());
            }
        }
        //one card placed to the discard pile
        discardPile.addCard(playingDeck.removeTop());
        //set actual rank and suit
        presentRank = discardPile.getCard().getRank();
        if(presentRank.equals(Rank.SVRSEK)) {
            //Svrsek means that actual suit is determined by the bottom card of
            //  the deck
            presentSuit = playingDeck.getLast().getSuit();
        } else {
            presentSuit = discardPile.getCard().getSuit();
        }
    }
    
    /**
     * Turn of the player on move; the player chooses one card and plays it
     * @param chosenCardIndex index of the card to be played. If the player
     * has to draw 1 or 2 cards, the index is -1 or -2 respectively.
     * 
     * @return GameState state of the game after the turn
     */
    @Override
    public GameState turn(int chosenCardIndex) {
        // chosenCardIndex is -1 -> draws one card
        if(chosenCardIndex == -1) {
            drawCards(playerOnMove, 1);            
            setNextPlayerOnMove();
            gameState = GameState.Continue;
        } else {
            Card playedCard;
            try {
                playedCard = getPlayerOnMove().getHand().getCard(chosenCardIndex);
            } catch(IndexOutOfBoundsException e) {
                //index out of bounds
                gameState = GameState.CardNotAllowed;
                return gameState;
            }
            // Check if the player can play this card
            boolean cardPlayable = checkCardAllowed(playedCard);
            if(cardPlayable) {
                // Play the card
                discardPile.addCard(getPlayerOnMove().getHand().removeCard(chosenCardIndex));
                // Check if the game is finished
                if (checkFinish()) {
                    gameState = GameState.Finish;
                    return gameState;
                }
                //Set new present rank, suit and player on move
                presentSuit = playedCard.getSuit();
                presentRank = playedCard.getRank();
                setNextPlayerOnMove();
                
                //Different cards cause different states
                playedCardResult(presentRank);
            } else {
                // Card is not allowed to play
                return gameState = GameState.CardNotAllowed;
            }
        }
    }
    
    private GameState playedCardResult(Rank playedCardRank) {
        switch (playedCardRank) {
            case SEDM : {
                sedmSuccession++;
                if (getPlayerOnMove().getHand().containsRank(Rank.SEDM)) {
                //Next player has SEDM in his hand - he can continue
                    gameState = GameState.Continue;
                } else {
                    //Next player has to draw two cards
                    drawCards(playerOnMove, 2 * sedmSuccession);
                    sedmSuccession = 0;
                    gameState = GameState.NextDrawsTwo;                            
                }
                break;
            }
            case SVRSEK : {
                gameState = GameState.ChangeSuit;
            }
            case ESO : {
                if (getPlayerOnMove().getHand().containsRank(Rank.ESO)) {
                //Next player has ESO in his hand - he can continue

            } else {
                //Next player cannot play one round
                System.out.println("Next player waits one round.");
                setNextPlayerOnMove();
                break;
            }
                gameState = GameState.StopNextPlayer;
            }
            default : return gameState = GameState.Continue;
        }
    }

    /**
     * Draw card(s) from the playing deck
     * @param player player that draws
     * @param int number of cards to draw
     */
    @Override
    public void drawCards(Player player, int numberOfCards) {
        for (int i = 0; i < numberOfCards; i++) {
            if (playingDeck.isEmpty()) {
                discardPileToDeck();
            }
            player.getHand().addCard(playingDeck.removeTop());
        }
    }
    
    /**
     * Moves cards (all except the top) from discard pile to playing deck
     * and shuffles the deck.
     */
    private void discardPileToDeck() {
        playingDeck.addCollection(discardPile.removeAllExceptTop());
        playingDeck.shuffle();
    }
    
    
    
    /**
     * Tests the validity of the turn - whether the card is allowed to be played
     * or not (according to the present settings and rules)
     * @param playedCard - card that has been played
     * @return true if the turn is allowed
     */
    private boolean checkCardAllowed(Card playedCard) {
        //TODO How to display the reasons if not allowed
        //Precedent player played SEDM -> next player can play SEDM
        if(gameState.equals(GameState.NextDrawsTwo)) {
            return playedCard.getRank().equals(presentRank);
        }
        //SVRSEK can play anytime
        if(playedCard.getRank().equals(Rank.SVRSEK)) {
            return true;
        }
        //Other cases
        return playedCard.getSuit().equals(presentSuit)
                || playedCard.getRank().equals(presentRank);        
    }

    /**
     * Checks whether the game finished (according to the settings and game rules)
     * @return true if the game is finished - if the player on move has no
     * cards in hand
     * version 0.1
     */
    private boolean checkFinish() {
        //Implement other possible rules
        return getPlayerOnMove().getHand().isEmpty();
    }
    
    /**
     * Sets next player as the player on move
     */
    public void setNextPlayerOnMove() {        
        playerOnMove = getNextPlayer();
    }
    
    /**
     * Get the last card played - the one that is lying on the top of
     * the discard pile
     * @return the last played card
     */
    @Override
    public Card getLastPlayedCard() {
        return discardPile.getCard();
    }
    
    /**
     * Get present rank - rank the players are allowed to play
     * @return present rank
     */
    @Override
    public Rank getPresentRank() {
        return presentRank;
    }
    
    /**
     * Get present suit - suit the players are allowed to play
     * @return present suit
     */
    @Override
    public Suit getPresentSuit() {
        return presentSuit;
    }
    
    /**
     * Set new present suit - suit the players are allowed to play
     * @param newSuit
     */
    @Override
    public void setPresentSuit(Suit newSuit) {
        presentSuit = newSuit;
    }

    /**
     * Get player that is on move
     * @return the playerOnMove
     */
    @Override
    public Player getPlayerOnMove() {
        return playerOnMove;
    }

    /**
     * Get next player (player who plays after the player that is on move)
     * @return next player
     */
    @Override
    public Player getNextPlayer() {
       //Index of the next player
       int index = (players.indexOf(getPlayerOnMove()) + 1) % players.size();
       return players.get(index); 
    }
    
}
